namespace RL

open RL.Components

module Random =
    let MakeRandomState =
        let rand = System.Random()
        rand.NextInt64() |> uint64

    /// A pseudo-random number generator built off of the splitmix64 algorithm.
    type RandomNumberGenerator(s : uint64) =
        let mutable state = s

        member private _.nextInt =
            let p1 r = (r ^^^ (r >>> 30)) * 0xbf58476d1ce4e5b9UL
            let p2 r = (r ^^^ (r >>> 27)) * 0x94d049bb133111ebUL
            let p3 r = r ^^^ (r >>> 31)
            state <- state + (0x9e3779b97f4a7c15UL)
            state |> p1 |> p2 |> p3
        member private r.nextFloat =
            (float(r.nextInt) / 2.0 ** 64)

        member _.GetState = state
        member r.Next(low : int, high : int) =
            floor((r.nextFloat * float(high - low)) + float low) |> int

module Helpers =
    let distance2DSq(x1 : int, x2 : int, y1 : int, y2 : int) =
        let dx = single((max x1 x2) - (min x1 x2))
        let dy = single((max y1 y2) - (min y1 y2))
        (dx * dx) + (dy * dy)
    let distance2D(x1 : int, x2 : int, y1 : int, y2 : int) =
        sqrt <| distance2DSq(x1, x2, y1, y2)

    // Returns a list of points in a diagonal line.
    let pointLineDiag(p1 : Point2D, p2 : Point2D) =
        let dx, dy = p2.x - p1.x, p2.y - p1.y
        let N = abs(dx) |> max <| abs(dy)
        let divN = if N = 0 then 0.0F else 1.0F / single N
        let xstep, ystep = single dx * divN, single dy * divN
        let mutable x, y = single p1.x, single p1.y

        [for _step in 0..N do
            let pt = { x = int(round x); y = int(round y) }
            x <- x + xstep
            y <- y + ystep
            yield pt]

    // Returns a list of points using only orthagonal points.
    let pointLineOrtho(p1 : Point2D, p2 : Point2D) =
        let dx, dy = p2.x - p1.x, p2.y - p1.y
        let nx, ny = abs dx, abs dy
        let sign_x, sign_y = (if dx > 0 then 1 else -1), (if dy > 0 then 1 else -1)

        let mutable px, py = p1.x, p1.y
        let mutable ix, iy = 0, 0

        [while ix < nx || iy < ny do
            if (1 + 2 * ix) * ny < (1 + 2 * iy) * nx then
                px <- px + sign_x
                ix <- ix + 1
            else
                py <- py + sign_y
                iy <- iy + 1
            yield { x = px; y = py }]
